package com.example;

import java.util.Optional;
import java.util.Set;
import javax.swing.*;

import com.baomidou.plugin.idea.mybatisx.dom.model.Mapper;
import com.baomidou.plugin.idea.mybatisx.util.MapperUtils;
import com.intellij.codeInsight.navigation.NavigationUtil;
import com.intellij.icons.AllIcons;
import com.intellij.lang.annotation.AnnotationHolder;
import com.intellij.lang.annotation.Annotator;
import com.intellij.lang.annotation.HighlightSeverity;
import com.intellij.openapi.actionSystem.AnAction;
import com.intellij.openapi.actionSystem.AnActionEvent;
import com.intellij.openapi.editor.markup.GutterIconRenderer;
import com.intellij.openapi.project.Project;
import com.intellij.psi.PsiClass;
import com.intellij.psi.PsiClassType;
import com.intellij.psi.PsiElement;
import com.intellij.psi.PsiExpression;
import com.intellij.psi.PsiLiteralExpression;
import com.intellij.psi.PsiLocalVariable;
import com.intellij.psi.PsiMethodCallExpression;
import com.intellij.psi.PsiReferenceExpression;
import com.intellij.psi.PsiType;
import com.intellij.psi.util.PsiTreeUtil;
import com.intellij.psi.util.PsiUtil;
import com.intellij.psi.xml.XmlTag;
import org.jetbrains.annotations.NotNull;

public class SqlSessionMethodAnnotator implements Annotator {

    @Override
    public void annotate(@NotNull PsiElement element, @NotNull AnnotationHolder holder) {
        if (!(element instanceof PsiMethodCallExpression)) {
            return;
        }

        PsiMethodCallExpression methodCall = (PsiMethodCallExpression) element;
        String methodName = methodCall.getMethodExpression().getReferenceName();

        // 检查是否是 MyBatis 方法调用
        if (!isMyBatisMethod(methodCall, methodName)) {
            return;
        }

        PsiExpression[] args = methodCall.getArgumentList().getExpressions();
        if (args.length == 0) {
            return;
        }
        String statementName = resolveStatementName(args[0], methodCall);
        if (statementName != null) {
            holder.newSilentAnnotation(HighlightSeverity.INFORMATION)
                .range(methodCall.getMethodExpression())  // 高亮方法名
                .gutterIconRenderer(new SqlMapperNavigator(statementName, methodCall.getProject()))
                .tooltip("点击跳转到 Mapper XML: " + statementName)
                .create();
        }
    }

    private boolean isMyBatisMethod(PsiMethodCallExpression methodCall, String methodName) {
        // 检查方法名是否在支持的列表中
        if (!Set.of("selectOne", "selectList", "insert", "update", "delete").contains(methodName)) {
            return false;
        }

        // 检查调用者是否是 sqlSession
        PsiExpression qualifier = methodCall.getMethodExpression().getQualifierExpression();
        return qualifier != null && isImplementingInterface(qualifier, "org.apache.ibatis.session.SqlSession");
    }
    // 增强的接口实现检测
    private boolean isImplementingInterface(PsiExpression expression, String interfaceFQN) {
        PsiType type = expression.getType();
        if (type == null) return false;

        PsiClass psiClass = PsiUtil.resolveClassInType(type);
        if (psiClass == null) return false;

        // 检查类本身或父类是否实现目标接口
        for (PsiClassType implemented : psiClass.getImplementsListTypes()) {
            if (interfaceFQN.equals(implemented.getCanonicalText())) {
                return true;
            }
        }

        // 检查继承链
        PsiClass superClass = psiClass.getSuperClass();
        while (superClass != null) {
            for (PsiClassType implemented : superClass.getImplementsListTypes()) {
                if (interfaceFQN.equals(implemented.getCanonicalText())) {
                    return true;
                }
            }
            superClass = superClass.getSuperClass();
        }

        return false;
    }

    private String resolveStatementName(PsiExpression arg, PsiMethodCallExpression methodCall) {
        // 处理字符串字面量
        if (arg instanceof PsiLiteralExpression) {
            Object value = ((PsiLiteralExpression) arg).getValue();
            if (value instanceof String) {
                String genericTypeName = resolveGenericTypeName(methodCall);
                return genericTypeName != null ? genericTypeName + "." + (String) value : null;
            }
        }

        // 处理 getStatementPrefix 方法调用
        if (arg instanceof PsiMethodCallExpression) {
            PsiMethodCallExpression prefixCall = (PsiMethodCallExpression) arg;
            PsiExpression[] prefixArgs = prefixCall.getArgumentList().getExpressions();
            if (prefixArgs.length > 0 && prefixArgs[0] instanceof PsiLiteralExpression) {
                Object suffixValue = ((PsiLiteralExpression) prefixArgs[0]).getValue();
                if (suffixValue instanceof String) {
                    String genericTypeName = resolveGenericTypeName(methodCall);
                    return genericTypeName != null ? genericTypeName + "." + suffixValue : null;
                }
            }else if(prefixArgs.length > 0 && prefixArgs[0] instanceof PsiReferenceExpression){
                final PsiElement resolve = ((PsiReferenceExpression) prefixArgs[0]).resolve();
                if (resolve instanceof PsiLocalVariable) {
                    String suffixValue = getVariableValue((PsiLocalVariable) resolve);
                    String genericTypeName = resolveGenericTypeName(methodCall);
                    return genericTypeName != null ? genericTypeName + "." + suffixValue : null;
                }
            }
        }
        return null;
    }

    public static String getVariableValue(PsiLocalVariable variable) {
        if (variable == null) return null;

        PsiExpression initializer = variable.getInitializer();
        if (initializer == null) return null;

        // 处理字面量
        if (initializer instanceof PsiLiteralExpression) {
            Object value = ((PsiLiteralExpression) initializer).getValue();
            return value instanceof String ? (String) value : null;
        }

        // 返回表达式文本（适用于其他情况）
        return initializer.getText();
    }

    private String resolveGenericTypeName(PsiMethodCallExpression methodCall) {
        PsiClass containingClass = PsiTreeUtil.getParentOfType(methodCall, PsiClass.class);
        if (containingClass == null) {
            return null;
        }

        PsiClassType[] superTypes = containingClass.getSuperTypes();
        if (superTypes.length == 0) {
            return null;
        }
        return superTypes[1].getCanonicalText();
    }

    // 跳转逻辑（封装为 GutterIconRenderer）
    private static class SqlMapperNavigator extends GutterIconRenderer {
        private final String statementName;
        private final Project project;

        public SqlMapperNavigator(String statementName, Project project) {
            this.statementName = statementName;
            this.project = project;
        }

        @Override
        public Icon getIcon() {
            return AllIcons.Gutter.ImplementedMethod; // 使用 IDEA 自带图标
        }

        @Override
        public boolean isNavigateAction() {
            return true;
        }

        @Override
        public AnAction getClickAction() {
            return new AnAction() {
                @Override
                public void actionPerformed(@NotNull AnActionEvent e) {
                    final int i = statementName.lastIndexOf(".");
                    if (i == -1) {
                        return;
                    }
                    String namespace = statementName.substring(0, i);
                    String sqlId = statementName.substring(i+1);
                    final Optional<Mapper> mapper = MapperUtils.findFirstMapper(project, namespace);
                    if (mapper.isPresent()) {
                        final XmlTag tagByMapper = MapperUtils.findTagByMapper(mapper.get(), sqlId);
                        if (tagByMapper != null) {
                            NavigationUtil.activateFileWithPsiElement(tagByMapper, true);
                        }
                    }
                }
            };
        }

        @Override
        public boolean equals(Object obj) {
            return obj instanceof SqlMapperNavigator &&
                statementName.equals(((SqlMapperNavigator)obj).statementName);
        }

        @Override
        public int hashCode() {
            return statementName.hashCode();
        }
    }
}